// CyTargetLinker,
// a Cytoscape plugin to extend biological networks with regulatory interaction
//
// Copyright 2011-2013 Department of Bioinformatics - BiGCaT, Maastricht University
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
package cytargetlinker;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.swing.JLabel;
import javax.swing.SwingConstants;

import cytargetlinker.data.RIN;
import cytargetlinker.data.ResultingNetwork;
import cytargetlinker.data.UserNetwork;
import cytargetlinker.dialogs.CyTargetLinkerDialog;
import cytoscape.CyNetwork;
import cytoscape.Cytoscape;
import cytoscape.data.CyAttributes;
import cytoscape.layout.CyLayoutAlgorithm;
import cytoscape.layout.CyLayouts;
import cytoscape.task.ui.JTaskConfig;
import cytoscape.task.util.TaskManager;
import cytoscape.view.CyNetworkView;
import cytoscape.view.cytopanels.CytoPanelState;
import cytoscape.visual.CalculatorCatalog;
import cytoscape.visual.VisualMappingManager;
import cytoscape.visual.VisualStyle;

/**
 * main class of the plugin that collects the data 
 * and distributes the different tasks to the right classes
 */
public class CyTargetLinker implements PropertyChangeListener {

	private CyTargetLinkerPlugin plugin;
	private UserNetwork userNetwork;
	private ResultingNetwork resultNetwork;
	
	private List<RIN> rins;
	
	// message for RIN import
	private String message = "";
	
	// visual style of the new network
	// TODO: different color of nodes for different edge types (e.g. miRNA and transcription factors)
	private VisualStyle visualStyle;
	
	// tab that contains the result information
	private ResultTab tab;
	// decides if the nodes in the user network should only be targets or sources or both
	private Direction direction = Direction.BOTH;
	
	private Map<RIN, JLabel> numInteractions;

	public enum Direction {
		SOURCE,
		TARGET,
		BOTH
	}
	
	public CyTargetLinker() {
		Cytoscape.getPropertyChangeSupport().addPropertyChangeListener(Cytoscape.NETWORK_DESTROYED, this);
		rins = new ArrayList<RIN>();
	}
	
	public CyTargetLinker(CyTargetLinkerPlugin plugin) {
		this.plugin = plugin;
		Cytoscape.getPropertyChangeSupport().addPropertyChangeListener(Cytoscape.NETWORK_DESTROYED, this);
		rins = new ArrayList<RIN>();
		numInteractions = new HashMap<RIN, JLabel>();
	}
	
	/**
	 * opens the load interactions dialog
	 */
	public void openDialog() {
		userNetwork = null;
		resultNetwork = null;
		rins.clear();
		
		CyTargetLinkerDialog dialog = new CyTargetLinkerDialog(this);
		dialog.setVisible(true);
	}
	
	/**
	 * starts the merge task (add interactions from the 
	 * RINs into the resulting network)
	 * @param rins
	 */
	public void startDataIntegration(List<File> files) {
		IntegrationTask task = new IntegrationTask(this, files);
		JTaskConfig jTaskConfig = new JTaskConfig();
		jTaskConfig.setOwner(Cytoscape.getDesktop());
		jTaskConfig.displayCloseButton(true);
		jTaskConfig.displayCancelButton(true);
		jTaskConfig.displayStatus(true);
		jTaskConfig.setAutoDispose(false);
		
		// Execute Task in New Thread; pops open JTask Dialog Box.
		TaskManager.executeTask(task, jTaskConfig);
		
		Cytoscape.getDesktop().getCytoPanel(SwingConstants.EAST).setState(CytoPanelState.DOCK);
	}
	
	/**
	  * checks if the selected identifierAttribute is a String attribute
	  * @return true if it is a String attribute, false if not
	  */
	public boolean checkIdAttribute(String idAttribute) {
		if(!idAttribute.equals("")) {
			CyAttributes cyNodeAttrs = Cytoscape.getNodeAttributes();
			byte type = cyNodeAttrs.getType(idAttribute);
			if(type == CyAttributes.TYPE_STRING) return true;
	 	}
		return false;
	 }

	/**
	 * creates a user network object
	 * creates a list of node ids
	 */
	public void createUserNetwork(CyNetwork network, String idAttribute) {
		userNetwork = new UserNetwork();
		userNetwork.setNetwork(network);
		userNetwork.setIdAttribute(idAttribute);
		userNetwork.setName(network.getTitle());
		userNetwork.processNodeIds();
	}
	
	/**
	 * creates a result network object
	 * checks the name in the set name method
	 * copies the user network into a new network
	 */
	public void createResultingNetwork(String name) {
		resultNetwork = new ResultingNetwork();
		resultNetwork.setName(name);
	}
	
	/**
	 * adds regulatory interactions to the resulting network
	 */
	public void mergeNetworks() {	
		
		resultNetwork.createNodeTypeAttribute();
		resultNetwork.addNewEdges(rins, direction, userNetwork);

		// apply treshhold = 1
		RINHandler.getHandler().applyThreshold(this);
		
		// apply force directed layout
		applyLayout();
		
		// creates CyTargetLinker visual style
		createVisualStyle();
	}
	
	public void applyLayout() {
		int count = resultNetwork.getNetwork().edgesList().size();
		if(count < 10000) {
			CyLayoutAlgorithm alg = CyLayouts.getLayout("force-directed");
			alg.doLayout();
		}
	}
	
	public void applyVisualStyle() {
		CyTargetLinkerVisualStyle style = new CyTargetLinkerVisualStyle();	
		Cytoscape.getNetworkView(resultNetwork.getNetwork().getIdentifier()).applyVizmapper(style.createVisualStyle(this));
		Cytoscape.createNetworkView(resultNetwork.getNetwork(), "CyTargetLinkerView");
	}
	
	public void updateTextFields() {
		if(!numInteractions.isEmpty()) {
			for(RIN rin : rins) {
				numInteractions.get(rin).setText(rin.getEdgesVisible().size() + " / " + rin.getTotal());
			}
		}
	}

	private int treshhold = 1;
	
	public int getThreshold() {
		return treshhold;
	}

	public void setTreshhold(int treshhold) {
		this.treshhold = treshhold;
	}


	public void createVisualStyle() {
		
		CyNetworkView view = Cytoscape.getNetworkView(resultNetwork.getName());
		
		// get the VisualMappingManager and CalculatorCatalog
		VisualMappingManager manager = Cytoscape.getVisualMappingManager();
		CalculatorCatalog catalog = manager.getCalculatorCatalog();

		// check to see if a visual style with this name already exists
		visualStyle = catalog.getVisualStyle(Preferences.getInstance().getVsName());
		if (visualStyle == null) {
			// if not, create it and add it to the catalog
			CyTargetLinkerVisualStyle style = new CyTargetLinkerVisualStyle();

			visualStyle = style.createVisualStyle(this);
			catalog.addVisualStyle(visualStyle);
		}

		view.setVisualStyle(visualStyle.getName()); // not strictly necessary

		// actually apply the visual style
		manager.setVisualStyle(visualStyle);
	}
	

	/**
	 * adds the tab to the result panel
	 */
	public void createColorTab() {
		tab = new ResultTab(this);
		tab.fillTab();
	}

	/**
	 * removes the tab in the result panel 
	 * if the network is destroyed
	 */
	@Override
	public void propertyChange(PropertyChangeEvent event) {
		if (event.getPropertyName().equalsIgnoreCase(Cytoscape.NETWORK_DESTROYED)) {
			if(resultNetwork.getNetwork() != null) {
				String id = resultNetwork.getNetwork().getIdentifier();
				if(event.getNewValue().equals(id)) {
					tab.removeTab();
					resultNetwork = null;
					userNetwork = null;
				}
			}
		}
	}
	
	/** SETTERS & GETTERS **/
	
	public CyTargetLinkerPlugin getPlugin() {
		return plugin;
	}

	public void setPlugin(CyTargetLinkerPlugin plugin) {
		this.plugin = plugin;
	}

	public UserNetwork getUserNetwork() {
		return userNetwork;
	}

	public void setUserNetwork(UserNetwork userNetwork) {
		this.userNetwork = userNetwork;
	}

	public ResultingNetwork getResultNetwork() {
		return resultNetwork;
	}

	public void setResultNetwork(ResultingNetwork resultNetwork) {
		this.resultNetwork = resultNetwork;
	}

	public List<RIN> getRins() {
		return rins;
	}

	public void setRins(List<RIN> rins) {
		this.rins = rins;
	}

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public VisualStyle getVisualStyle() {
		return visualStyle;
	}

	public void setVisualStyle(VisualStyle visualStyle) {
		this.visualStyle = visualStyle;
	}

	public ResultTab getTab() {
		return tab;
	}

	public void setTab(ResultTab tab) {
		this.tab = tab;
	}

	public Direction getDirection() {
		return direction;
	}

	public void setDirection(Direction direction) {
		this.direction = direction;
	}

	public int getTreshhold() {
		return treshhold;
	}

	public Map<RIN, JLabel> getNumInteractions() {
		return numInteractions;
	}

	public void setNumInteractions(Map<RIN, JLabel> numInteractions) {
		this.numInteractions = numInteractions;
	}
}